#pragma once
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <system_error> 

#include <cstring>
#include <stdexcept>
#include <string>
#include <vector>

namespace uds {

// 通用异常
inline void throw_errno(const char* what) {
    throw std::system_error(errno, std::system_category(), what);
}

// 基类：负责 fd 生命周期
class socket_base {
public:
    explicit socket_base(int type = SOCK_STREAM) {
        fd_ = ::socket(AF_UNIX, type, 0);
        if (fd_ == -1) throw_errno("socket");
    }
    socket_base(socket_base&& rhs) noexcept : fd_(rhs.fd_) { rhs.fd_ = -1; }
    socket_base& operator=(socket_base&& rhs) noexcept {
        if (this != &rhs) { close(); fd_ = rhs.fd_; rhs.fd_ = -1; }
        return *this;
    }
    ~socket_base() { close(); }
    int native() const { return fd_; }
protected:
    void close() {
        if (fd_ != -1) { ::close(fd_); fd_ = -1; }
    }
    int fd_;
};

// 服务器端
class stream_server : public socket_base {
public:
    explicit stream_server(const std::string& path)
        : socket_base(SOCK_STREAM), path_(path) {
        unlink(path_.c_str());          // 清理残留
        sockaddr_un addr{};
        addr.sun_family = AF_UNIX;
        std::strncpy(addr.sun_path, path_.c_str(), sizeof(addr.sun_path) - 1);
        if (::bind(fd_, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1)
            throw_errno("bind");
        if (::listen(fd_, 5) == -1) throw_errno("listen");
    }
    ~stream_server() { unlink(path_.c_str()); }
    class stream_socket accept();
private:
    std::string path_;
};

// 客户端连接后的 socket
class stream_socket : public socket_base {
public:
    stream_socket() = default;                      // 空对象
    explicit stream_socket(int fd) : socket_base() { fd_ = fd; }  // 内部使用
    void send(const void* data, std::size_t len) {
        std::size_t sent = 0;
        while (sent < len) {
            ssize_t n = ::write(fd_, static_cast<const char*>(data) + sent, len - sent);
            if (n == -1) throw_errno("write");
            sent += n;
        }
    }
    std::size_t recv(void* buf, std::size_t len) {
        ssize_t n = ::read(fd_, buf, len);
        if (n == -1) throw_errno("read");
        return static_cast<std::size_t>(n);   // 0 表示对端关闭
    }
    void writeLine(const std::string& line) {
        std::string tmp = line + '\n';
        send(tmp.data(), tmp.size());
    }
    std::string readLine() {
        std::string s;
        char c;
        while (recv(&c, 1) == 1) {
            if (c == '\n') break;
            s += c;
        }
        return s;
    }
};

inline stream_socket stream_server::accept() {
    int cli = ::accept(fd_, nullptr, nullptr);
    if (cli == -1) throw_errno("accept");
    return stream_socket(cli);
}

// 客户端主动连接
inline stream_socket connect(const std::string& path) {
    stream_socket sock;
    sockaddr_un addr{};
    addr.sun_family = AF_UNIX;
    std::strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path) - 1);
    if (::connect(sock.native(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1)
        throw_errno("connect");
    return sock;
}

} // namespace uds
